using System;
using System.Collections.Generic;
using System.Text;
using CommonTypes;
using CommonTypes.Math.Geometry3D;
using CommonTypes.Math;


namespace BuildingBasicDomain
{
    //this is temporal  code dublication .in order to move some geometry methods from the ILine ,Area ... here .
    public class GeometryLibrary
    {
        public static IPoint  IntersectsLine( ILine line1, ILine line2, Geometry.Type_of_Intersection typeOfIntersection)
        {
            System.Diagnostics.Debug.Assert(line1 != null);
            // return IntersectionLine2D(_line, typeOfIntersection);
            return LineLineIntersect( line1 ,  line2);
        }

        [Obsolete("use LineLineIntersect", true)]
        private static double dist3D_Segment_to_Segment(ILine S1, ILine S2)
        {
            const double SMALL_NUM = 1.0f;

            Vector3D u = S1.ENode.V - S1.SNode.V;
            Vector3D v = S2.ENode.V - S2.SNode.V;
            Vector3D w = S1.SNode.V - S2.SNode.V;

            double a = Vector3D.DotProduct(u, u);        // always >= 0
            double b = Vector3D.DotProduct(u, v);
            double c = Vector3D.DotProduct(v, v);        // always >= 0
            double d = Vector3D.DotProduct(u, w);
            double e = Vector3D.DotProduct(v, w);

            double D = a * c - b * b;       // always >= 0
            double sc, sN, sD = D;      // sc = sN / sD, default sD = D >= 0
            double tc, tN, tD = D;      // tc = tN / tD, default tD = D >= 0

            // compute the _line parameters of the two closest points
            if (D < SMALL_NUM)
            { // the lines are almost parallel
                sN = 0.0;        // force using point P0 on segment S1
                sD = 1.0;        // to prevent possible division by 0.0 later
                tN = e;
                tD = c;
            }
            else
            {                // get the closest points on the infinite lines
                sN = (b * e - c * d);
                tN = (a * e - b * d);
                if (sN < 0.0)
                {       // sc < 0 => the s=0 edge is visible
                    sN = 0.0;
                    tN = e;
                    tD = c;
                }
                else if (sN > sD)
                {  // sc > 1 => the s=1 edge is visible
                    sN = sD;
                    tN = e + b;
                    tD = c;
                }
            }

            if (tN < 0.0)
            {           // tc < 0 => the t=0 edge is visible
                tN = 0.0;
                // recompute sc for this edge
                if (-d < 0.0)
                    sN = 0.0;
                else if (-d > a)
                    sN = sD;
                else
                {
                    sN = -d;
                    sD = a;
                }
            }
            else if (tN > tD)
            {      // tc > 1 => the t=1 edge is visible
                tN = tD;
                // recompute sc for this edge
                if ((-d + b) < 0.0)
                    sN = 0;
                else if ((-d + b) > a)
                    sN = sD;
                else
                {
                    sN = (-d + b);
                    sD = a;
                }
            }
            // finally do the division to get sc and tc
            sc = (Math.Abs(sN) < SMALL_NUM ? 0.0 : sN / sD);
            tc = (Math.Abs(tN) < SMALL_NUM ? 0.0 : tN / tD);

            // get the difference of the two closest points
            Vector3D dP = w + (sc * u) - (tc * v);  // = S1(sc) - S2(tc)

            return (dP.GetLength());   // return the closest distance
        }

        public static double DistancePoint(ILine line ,  IPoint point)
        {
            Vector3D projection = Perpendicular(line, point);
            return projection.GetLength();
        }

        public IPoint IntersectsPlane(IPlane plane) { throw new NotImplementedException(); }

        /// <summary>
        /// The Line To  Line Intersection Algorithm in 3D.
        /// If You Do Not Like it Ovverde it .And Implement it .
        /// </summary>
        /// <param name="line1"></param>
        /// <param name="line2"></param>
        /// <returns></returns>
        public static IPoint LineLineIntersect(ILine line1, ILine line2)
        {
            const double EPS = 0.1;


            IPoint p1 = line1.SNode;
            IPoint p2 = line1.ENode;
            IPoint p3 = line2.SNode;
            IPoint p4 = line2.ENode;

            IPoint p13, p43, p21;
            double d1343, d4321, d1321, d4343, d2121;
            double numer, denom;

            double mua = 0, mub = 0;

            p13 = new Point(p1.X - p3.X, p1.Y - p3.Y, p1.Z - p3.Z);
            p43 = new Point(p4.X - p3.X, p4.Y - p3.Y, p4.Z - p3.Z);

            if (Math.Abs(p43.X) < EPS && Math.Abs(p43.Y) < EPS && Math.Abs(p43.Z) < EPS)
                return (null);
            p21 = new Point(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);

            if (Math.Abs(p21.X) < EPS && Math.Abs(p21.Y) < EPS && Math.Abs(p21.Z) < EPS)
                return (null);

            d1343 = p13.X * p43.X + p13.Y * p43.Y + p13.Z * p43.Z;
            d4321 = p43.X * p21.X + p43.Y * p21.Y + p43.Z * p21.Z;
            d1321 = p13.X * p21.X + p13.Y * p21.Y + p13.Z * p21.Z;
            d4343 = p43.X * p43.X + p43.Y * p43.Y + p43.Z * p43.Z;
            d2121 = p21.X * p21.X + p21.Y * p21.Y + p21.Z * p21.Z;

            denom = d2121 * d4343 - d4321 * d4321;
            if (Math.Abs(denom) < EPS)
                return (null);
            numer = d1343 * d4321 - d1321 * d4343;

            mua = numer / denom;
            mub = (d1343 + d4321 * (mua)) / d4343;

            Vector pa = new Vector();
            Vector pb = new Vector();
            pa.X = p1.X + mua * p21.X;
            pa.Y = p1.Y + mua * p21.Y;
            pa.Z = p1.Z + mua * p21.Z;
            pb.X = p3.X + mub * p43.X;
            pb.Y = p3.Y + mub * p43.Y;
            pb.Z = p3.Z + mub * p43.Z;

            pb = pa - pb;

            if (pb.Length() < EPS)
                return ((Point)pa);
            else return null;
        }

        public static IPoint ProjectionOnLine(ILine line ,  IPoint point)
        {
            System.Diagnostics.Debug.Assert(point != null);
            //MOVE THIS TO MATH LIBRARY 
            IPoint projectionPoint;
            Vector3D projection = point.V - line.SNode.V;
            Vector3D direction = line.Direction;
            direction.Normalize();
            projection.Normalize();
            double cos = Vector3D.DotProduct(direction, projection);
            projection = point.V - line.SNode.V;
            projection = direction * (projection.GetLength() * cos) + line.SNode.V;
            projectionPoint = (Point)projection;
            return projectionPoint;
        }

        public static Vector3D Perpendicular(ILine line ,  IPoint point)
        {
            IPoint projectionPoint = ProjectionOnLine(line,point);
            return point.V - projectionPoint.V;

        }

        public static bool IsLineInLine(ILine line1, ILine line2)
        {
            bool contains_point_or_whatever_ = line2.ContainsPointOpen(line1.SNode) || line2.ContainsPointOpen(line1.ENode) ||line1.ContainsPointOpen(line2.SNode) || line1.ContainsPointOpen(line2.ENode);

            if (contains_point_or_whatever_)
            {
                if (Vector3D .CrossProduct (line1.Direction ,line2.Direction).GetLength ()==0) return true ;
            }
            return false;
        }

        public static bool isAreaInArea(IArea iArea1, IArea iArea2)
        {
            foreach (IPoint point in iArea1.Polygon)
            {
                if (iArea2.containsPoint(point))
                    return false;

            }
            return true;
        }

        public static IEnumerable <ILine> GridAreaLines(IArea iArea, int p, Vector3D vector3D)
        {
           double[,] boundingRect  = iArea.Polygon.BoundingCube(1.1f);
           IPoint upperEdgePoint = new Point(boundingRect[0, 0], boundingRect[0, 1], boundingRect[0, 2]);
           IPoint LowwerEdgePoint = new Point(boundingRect[1, 0], boundingRect[1, 1], boundingRect[1, 2]);
           upperEdgePoint.V = upperEdgePoint.V - LowwerEdgePoint.V;

           OrientedBox box = new OrientedBox(new Vector3F((float)iArea.GetMiddle().X , (float)iArea.GetMiddle().Y, (float)iArea.GetMiddle().Z), boundingRect);


           PolyLine<Line> poly = (PolyLine<Line>)iArea.Polygon ;
           IPoint p1 = new Point(iArea.Polygon[0].X, iArea.Polygon[0].Y, iArea.Polygon[0].Z);

           Vector3D  vx =  poly  [0].Direction ;
           Vector3D vy = poly[poly.Count - 1].Direction;

           vx.Normalize(); vy.Normalize();
           List<ILine> lines = GeometryWorkBench.UniformLines(p1, vx, -vy, box.Extent3 ,box .Extent1, p);
           List<ILine> tirmedlines = new List<ILine>();

           IList<IPoint> points; IPoint  node2; IPoint  node1; 
           foreach (ILine line in lines)
           {
               points = poly.PointsIntersects(line);
               
               if (points.Count == 2)
               {
                   node1 = new Point (points[0].X, points[0].Y, points[0].Z);
                   node2 = new Point (points[1].X, points[1].Y, points[1].Z);
                   tirmedlines.Add(new Line(node1, node2));
         

   
               }

           }


           return tirmedlines   ;//GetEnumerator();

        }
    }
}
